package org.grapes.wine.api.client.method.impl;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.util.HashMap;
import java.util.Map;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

import org.apache.commons.httpclient.HttpException;
import org.apache.log4j.Logger;
import org.dom4j.DocumentException;
import org.dom4j.io.SAXReader;
import org.grapes.wine.api.bean.MethodErrorBean;
import org.grapes.wine.api.client.exception.WineException;
import org.grapes.wine.api.client.param.RestClientConfig;
import org.grapes.wine.api.client.util.PostBean;
import org.grapes.wine.api.method.EWineMethod;
import org.xml.sax.SAXException;

public class CallWineMethod {

	static Logger logger = Logger.getLogger(CallWineMethod.class);
	
	protected final String apiId;
	protected final String sessionKey;
	protected final String sig;
	protected final String serverUrl;
	private final static int MAX_RETRYCOUNT = 32;

	public CallWineMethod(String apiId, String sessionKey, String sig) {
		this.apiId = apiId;
		this.sessionKey = sessionKey;
		this.sig = sig;
		serverUrl = null;
	}

	public CallWineMethod(String apiId, String sessionKey, String sig, String serverUrl) {
		this.apiId = apiId;
		this.sessionKey = sessionKey;
		this.sig = sig;
		this.serverUrl = serverUrl;
	}

	public Object callMethod(EWineMethod method,
			Map<String, String> fixedParams) throws WineException, IOException,
			SAXException {
		// user should have a api key and session key to get data
		fixedParams.put("api_id", apiId);
		fixedParams.put("session_key", sessionKey);
		fixedParams.put("sig", sig);
		return getObjectByJAXB(method, fixedParams);
		// return getDocument(method, fixedParams);
	}

	private Object getObjectByJAXB(EWineMethod method, Map<String,String> param) throws WineException {
		int retryCount = 0;
		try {
			// ensure the methodName is existed
			InputStream istream = openInputStream(method.methodName(), param);
			JAXBContext context = JAXBContext.newInstance("org.grapes.wine.api.bean");
			Unmarshaller unmarshaller = context.createUnmarshaller();
			MethodErrorBean errorBean;
			Object bean = unmarshaller.unmarshal(istream);
			if (bean instanceof MethodErrorBean) {
				errorBean = (MethodErrorBean) bean;
				int errorCode = errorBean.getErrorCode();
				String msg = errorBean.getDescription();
				throw new WineException(errorCode, msg);
			}
		
			return bean;
		} catch (HttpException e) {
			retryCount++;
			if (retryCount == MAX_RETRYCOUNT) {
				throw new WineException("Too many times retry");
			}
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (JAXBException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}
	
	@Deprecated
	private org.dom4j.Document getDocument(EWineMethod method,
			Map<String, String> params) throws MalformedURLException,
			SAXException, WineException {
		int retryCount = 0;
		org.dom4j.Document doc = org.dom4j.DocumentHelper.createDocument();
		try {
			InputStream istream = openInputStream(method.methodName(), params);
			doc = new SAXReader().read(istream);
		} catch (DocumentException e) {
			e.printStackTrace();
		} catch (HttpException e) {
			retryCount++;
			if (retryCount == MAX_RETRYCOUNT) {
				throw new WineException("Too many times retry");
			}
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return doc;
	}

	private InputStream openInputStream(String method,
			Map<String, String> fixedParams) throws HttpException, IOException {

		PostBean postBean = BasePostBean(method);
		postBean.addParameters(fixedParams);
		return postBean.getPostAsStream();
	}

	private PostBean BasePostBean(String method) {
		Map<String, String> params = new HashMap<String, String>();
		params.put("method", method);
		PostBean postBean = new PostBean();
		System.out.println(serverUrl);
		postBean.setUrl((serverUrl == null) ? RestClientConfig.SERVER_ADDR
				: serverUrl);
		postBean.setParameters(params);
		return postBean;
	}
}